﻿using System;
using System.Collections.Generic;
using fastCSharp;
using fastCSharp.threading;
using System.Threading;

namespace OnlineJudge
{
    /// <summary>
    /// OJ路由
    /// </summary>
    class Router : fastCSharp.net.tcp.commandClient<OnlineJudge.tcpClient.Server.tcpClient>.router<JudgeCount>
    {
        /// <summary>
        /// OJ路由服务
        /// </summary>
        private RouterServer routerServer;
        /// <summary>
        /// 最小负载位置
        /// </summary>
        private int currentIndex;
        /// <summary>
        /// 主服务索引位置
        /// </summary>
        private int mainIndex;
        /// <summary>
        /// OJ路由
        /// </summary>
        /// <param name="routerServer"></param>
        public Router(RouterServer routerServer)
            : base((attribute, verifyMethod, verify) => new OnlineJudge.tcpClient.Server.tcpClient(attribute, verifyMethod))
        {
            this.routerServer = routerServer;
            start();
        }
        protected override void onCreated(OnlineJudge.tcpClient.Server.tcpClient client, fastCSharp.net.tcp.host host)
        {
            try
            {
                JudgeCount judgeCount = new JudgeCount(routerServer, this, client, host);
                if (judgeCount.Client != null)
                {
                    Monitor.Enter(clientLock);
                    if (isDisposed == 0)
                    {
                        for (int index = hostCount; index != count; )
                        {
                            if (clients[--index].Host.Equals(ref host))
                            {
                                clients[index].Host = clients[count].Host;
                                clients[count].Set(client, ref host, judgeCount);
                                if (judgeCount.Config.IsMain) mainIndex = count;
                                else if (currentIndex < count && clients[currentIndex].Custom.Count >= 0) currentIndex = count;
                                ++count;
                                Monitor.Exit(clientLock);
                                client = null;
                                Func<fastCSharp.net.returnValue<bool>, bool> checkProblemTime = routerServer.CheckProblemTime;
                                if (checkProblemTime != null)
                                {
                                    try
                                    {
                                        checkProblemTime(true);
                                    }
                                    catch { }
                                }
                                newClient(judgeCount);
                                output(date.NowSecond.toString() + " -> " + host.Host + ":" + host.Port.toString() + " 连接成功" + (judgeCount.Config.IsMain ? " Main" : null), ConsoleColor.White);
                                return;
                            }
                        }
                    }
                    Monitor.Exit(clientLock);
                    return;
                }
            }
            catch { }
            finally
            {
                pub.Dispose(ref client);
            }
            base.onCreated(null, host);
            output(date.NowSecond.toString() + " -> " + host.Host + ":" + host.Port.toString() + " 连接失败", ConsoleColor.Red);
        }
        /// <summary>
        /// 输出任务
        /// </summary>
        private static taskQueue outputQueue = new taskQueue();
        /// <summary>
        /// 输出
        /// </summary>
        /// <param name="message"></param>
        /// <param name="color"></param>
        private static void output(string message, ConsoleColor color)
        {
            outputQueue.Add(() =>
            {
                ConsoleColor oldColor = Console.ForegroundColor;
                Console.ForegroundColor = color;
                Console.WriteLine(message);
                Console.ForegroundColor = oldColor;
            });
        }
        /// <summary>
        /// 新增OJ调用客户端
        /// </summary>
        /// <param name="judgeCount"></param>
        private void newClient(JudgeCount judgeCount)
        {
            do
            {
                JudgeReportBase judge = routerServer.GetNextJudge();
                if (judge == null) return;
                Monitor.Enter(clientLock);
                if (judgeCount.TryJudgeCount())
                {
                    Monitor.Exit(clientLock);
                    if (this.judge(judgeCount, judge) == 0) return;
                }
                else
                {
                    Monitor.Exit(clientLock);
                    routerServer.ErrorJudge(judge);
                    return;
                }
            }
            while (true);
        }
        /// <summary>
        /// 提交测试
        /// </summary>
        /// <param name="judgeCount"></param>
        /// <param name="judge"></param>
        /// <returns>是否成功</returns>
        private int judge(JudgeCount judgeCount, JudgeReportBase judge)
        {
            try
            {
                if (judgeCount.Judge(judge)) return 1;
            }
            catch { }
            routerServer.ErrorJudge(judge);
            return 0;
        }
        /// <summary>
        /// 新增OJ调用客户端
        /// </summary>
        /// <param name="judgeCount"></param>
        public void OnJudged(JudgeCount judgeCount)
        {
            Monitor.Enter(clientLock);
            if (--judgeCount.Count <= 0)
            {
                Monitor.Exit(clientLock);
                newClient(judgeCount);
            }
            else Monitor.Exit(clientLock);
        }
        /// <summary>
        /// 获取OJ客户端
        /// </summary>
        /// <returns></returns>
        public JudgeCount GetMinClient()
        {
            JudgeCount value;
            Monitor.Enter(clientLock);
            if (count > 1)
            {
                if (mainIndex < count && (value = clients[mainIndex].Custom).TryJudgeCount())
                {
                    Monitor.Exit(clientLock);
                    return value;
                }
                if (currentIndex < count && currentIndex != mainIndex && (value = clients[currentIndex].Custom).TryJudgeCount())
                {
                    Monitor.Exit(clientLock);
                    return value;
                }
                int index = count - 1;
                value = clients[index].Custom;
                if (value.TryJudgeCount())
                {
                    currentIndex = index;
                    Monitor.Exit(clientLock);
                    return value;
                }
                do
                {
                    JudgeCount nextValue = clients[--index].Custom;
                    if (nextValue.TryJudgeCount())
                    {
                        currentIndex = index;
                        Monitor.Exit(clientLock);
                        return nextValue;
                    }
                    if (nextValue.Count < value.Count) value = nextValue;
                }
                while (index != 0);
                currentIndex = clients.Length;
                ++value.Count;
                Monitor.Exit(clientLock);
                return value;
            }
            if (count == 0)
            {
                Monitor.Exit(clientLock);
                return null;
            }
            ++(value = clients[0].Custom).Count;
            Monitor.Exit(clientLock);
            return value;
        }
        /// <summary>
        /// 获取任意OJ客户端
        /// </summary>
        /// <returns></returns>
        public JudgeCount GetRandomClient()
        {
            Monitor.Enter(clientLock);
            if (count != 0)
            {
                JudgeCount value = clients[random.Default.Next(count)].Custom;
                Monitor.Exit(clientLock);
                return value;
            }
            Monitor.Exit(clientLock);
            return null;
        }
        /// <summary>
        /// 获取主判题OJ客户端
        /// </summary>
        /// <returns></returns>
        public JudgeCount GetMainClient()
        {
            return clients[mainIndex].Custom;
        }
        /// <summary>
        /// 测试调用失败
        /// </summary>
        /// <param name="judgeCount"></param>
        public void Error(JudgeCount judgeCount)
        {
            output(date.NowSecond.toString() + " -> " + judgeCount.Host.Host + ":" + judgeCount.Host.Port.toString() + " 关闭连接", ConsoleColor.Red);
            Monitor.Enter(clientLock);
            --judgeCount.Count;
            for (int index = count; index != 0; )
            {
                if (clients[--index].Custom == judgeCount)
                {
                    clients[index] = clients[--count];
                    clients[count].Set(null, ref judgeCount.Host);
                    if (index == mainIndex) mainIndex = 0;
                    Monitor.Exit(clientLock);
                    new clientCreator(this, judgeCount.Host).Create();
                    return;
                }
            }
            Monitor.Exit(clientLock);
            judgeCount.Error();
        }
    }
}
